package com.iotechn.microunimall.admin.service;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.iotechn.microunimall.admin.api.service.open.RoleService;
import com.iotechn.microunimall.admin.api.enums.RoleStatusType;
import com.iotechn.microunimall.admin.mapper.RoleMapper;
import com.iotechn.microunimall.admin.mapper.RolePermissionMapper;
import com.iotechn.microunimall.core.annotation.HttpMethod;
import com.iotechn.microunimall.core.annotation.HttpOpenApi;
import com.iotechn.microunimall.admin.api.exception.AdminServiceException;
import com.iotechn.microunimall.core.exception.ExceptionDefinition;
import com.iotechn.microunimall.core.exception.ServiceException;
import com.iotechn.microunimall.admin.api.domain.RoleDO;
import com.iotechn.microunimall.admin.api.domain.RolePermissionDO;
import com.iotechn.microunimall.admin.api.dto.PermissionPointDTO;
import com.iotechn.microunimall.admin.api.dto.RoleSetPermissionDTO;
import com.iotechn.microunimall.core.model.Page;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by rize on 2019/4/11.
 */
@Service("roleService")
public class RoleServiceImpl implements RoleService, InitializingBean, ApplicationContextAware {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RolePermissionMapper rolePermissionMapper;

    public static List<PermissionPointDTO> permDTOs = new LinkedList<>();

    public static Set<String> allPermPoint = new HashSet<>();

    private ApplicationContext applicationContext;

    @Override
    public Page<RoleDO> list(String name, Integer page, Integer limit, Long adminId) throws ServiceException {
        Wrapper<RoleDO> wrapper = new EntityWrapper<RoleDO>();
        if (!StringUtils.isEmpty(name)) {
            wrapper.like("name", name);
        }
        List<RoleDO> roleDOS = roleMapper.selectPage(new RowBounds((page - 1) * limit, limit), wrapper);
        Integer count = roleMapper.selectCount(wrapper);
        return new Page<RoleDO>(roleDOS, page, limit, count);
    }

    @Override
    public RoleDO create(RoleDO roleDO, Long adminId) throws ServiceException {
        Date now = new Date();
        roleDO.setStatus(RoleStatusType.ACTIVE.getCode());
        roleDO.setGmtUpdate(now);
        roleDO.setGmtCreate(now);
        if (roleMapper.insert(roleDO) > 0) {
            return roleDO;
        }
        throw new AdminServiceException(ExceptionDefinition.ADMIN_UNKNOWN_EXCEPTION);
    }

    @Override
    public String delete(Long roleId, Long adminId) throws ServiceException {
        if (roleMapper.deleteById(roleId) > 0) {
            return "ok";
        }
        throw new AdminServiceException(ExceptionDefinition.ADMIN_UNKNOWN_EXCEPTION);
    }

    @Override
    public RoleDO update(RoleDO roleDO, Long adminId) throws ServiceException {
        if (roleMapper.updateById(roleDO) > 0) {
            return roleDO;
        }
        throw new AdminServiceException(ExceptionDefinition.ADMIN_UNKNOWN_EXCEPTION);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String permissionSet(RoleSetPermissionDTO roleSetPermissionDTO, Long adminId) throws ServiceException {
        Long roleId = roleSetPermissionDTO.getRoleId();
        if (roleId == null) {
            throw new AdminServiceException(ExceptionDefinition.PARAM_CHECK_FAILED);
        }
        rolePermissionMapper.delete(new EntityWrapper<RolePermissionDO>().eq("role_id", roleId));
        //构建插入
        List<String> permissions = roleSetPermissionDTO.getPermissions();
        if (!CollectionUtils.isEmpty(permissions)) {
            Date now = new Date();
            for (String permission : permissions) {
                RolePermissionDO rolePermissionDO = new RolePermissionDO();
                rolePermissionDO.setRoleId(roleId);
                rolePermissionDO.setDeleted(0);
                rolePermissionDO.setPermission(permission);
                rolePermissionDO.setGmtCreate(now);
                rolePermissionDO.setGmtUpdate(now);
                rolePermissionMapper.insert(rolePermissionDO);
            }
        }
        return "ok";
    }

    @Override
    public Map<String,Object> permissionList(Long roleId, Long adminId) throws ServiceException {
        List<RolePermissionDO> rolePermissionDOList =
                rolePermissionMapper.selectList(
                        new EntityWrapper<RolePermissionDO>()
                                .eq("role_id", roleId));

        Map<String,Object> map = new HashMap<>();
        Set<String> permissionPoint = new HashSet<>();
        for (RolePermissionDO permissionDO : rolePermissionDOList) {
            if ("*".equals(permissionDO.getPermission())) {
                //若为超级管理员，则直接push所有权限进去
                permissionPoint.addAll(allPermPoint);
                break;
            } else {
                permissionPoint.add(permissionDO.getPermission());
            }
        }
        map.put("systemPermissions", permDTOs);
        map.put("assignedPermissions", permissionPoint);
        return map;
    }

    @Override
    public List<Map<String, Object>> options(Long adminId) throws ServiceException {
        List<RoleDO> roleDOS = roleMapper.selectList(new EntityWrapper<>());
        List<Map<String,Object>> list = new LinkedList<>();
        roleDOS.forEach(item -> {
            Map<String,Object> map = new HashMap<>();
            map.put("value", item.getId());
            map.put("label", item.getName());
            list.add(map);
        });
        return list;
    }

    @Override
    public List<PermissionPointDTO> getAllPermissionPointDTO() throws ServiceException {
        return permDTOs;
    }

    @Override
    public Set<String> getAllPermissionPoint() throws ServiceException {
        return allPermPoint;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //扫描所有的Bean。提取相应权限点。
        String[] serviceList = applicationContext.getBeanNamesForAnnotation(Service.class);
        for (String name : serviceList) {
            Class<?>[] interfaces = applicationContext.getBean(name).getClass().getInterfaces();
            for (Class inter : interfaces) {
                Annotation annotation = inter.getDeclaredAnnotation(HttpOpenApi.class);
                if (annotation != null) {
                    HttpOpenApi httpOpenApi = (HttpOpenApi) annotation;
                    Method[] methods = inter.getMethods();
                    for (Method method : methods) {
                        HttpMethod httpMethod = method.getDeclaredAnnotation(HttpMethod.class);
                        if (httpMethod != null && !StringUtils.isEmpty(httpMethod.permission())) {
                            if (StringUtils.isEmpty(httpMethod.permissionParentName()) || StringUtils.isEmpty(httpMethod.permissionName())) {
                                throw new AdminServiceException(ExceptionDefinition.ADMIN_API_PERMISSION_REGISTER_FAILED);
                            }
                        }
                        boolean hasParent = false;
                        permDTOLoop: for (PermissionPointDTO pointDTO : RoleServiceImpl.permDTOs) {
                            if (pointDTO.getLabel().equals(httpMethod.permissionParentName())) {
                                //若已经存在父分组，则将其追加在后面即可
                                hasParent = true;
                                addChildPermissionPointDTO(pointDTO, httpMethod, httpOpenApi, method);
                                break permDTOLoop;
                            }
                        }
                        if (!hasParent) {
                            //若不存在父分组，则新建父分组
                            PermissionPointDTO parentDTO = new PermissionPointDTO();
                            parentDTO.setLabel(httpMethod.permissionParentName());
                            parentDTO.setId(httpMethod.permissionParentName());
                            parentDTO.setChildren(new LinkedList<>());
                            RoleServiceImpl.permDTOs.add(parentDTO);
                            //然后在parentDTO后面添加子类目，以及孙类目
                            addChildPermissionPointDTO(parentDTO, httpMethod, httpOpenApi, method);
                        }
                    }
                }
            }
        }
    }

    private void addChildPermissionPointDTO(PermissionPointDTO parentDTO, HttpMethod httpMethod, HttpOpenApi httpOpenApi, Method method) throws ServiceException {
        if (CollectionUtils.isEmpty(parentDTO.getChildren())) {
            parentDTO.setChildren(new LinkedList<>());
        }

        boolean hasChild = false;

        for (PermissionPointDTO childDTO : parentDTO.getChildren()) {
            if (childDTO.getLabel().equals(httpMethod.permissionName())) {
                //若存在
                hasChild = true;
                addGrandChildPermissionPointDTO(childDTO, httpMethod, httpOpenApi, method);
            }
        }
        if (!hasChild) {
            //添加child
            PermissionPointDTO childDTO = new PermissionPointDTO();
            childDTO.setId(httpMethod.permissionName());
            childDTO.setLabel(httpMethod.permissionName());
            childDTO.setChildren(new LinkedList<>());
            parentDTO.getChildren().add(childDTO);
            addGrandChildPermissionPointDTO(childDTO, httpMethod, httpOpenApi, method);
        }


    }

    private void addGrandChildPermissionPointDTO(PermissionPointDTO childDTO, HttpMethod httpMethod, HttpOpenApi httpOpenApi, Method method) throws ServiceException {
        //遍历权限点是否有重复
        for (PermissionPointDTO pointDTO : childDTO.getChildren()) {
            if (pointDTO.getId().equals(httpMethod.permission())) {
                //不允许重复权限点
//                throw new LauncherServiceException(LauncherExceptionDefinition.LAUNCHER_API_REGISTER_FAILED);
                return;
            }
        }
        //若无重复权限点
        PermissionPointDTO pointDTO = new PermissionPointDTO();
        RoleServiceImpl.allPermPoint.add(httpMethod.permission());
        pointDTO.setId(httpMethod.permission());
        pointDTO.setLabel(httpMethod.description());
        pointDTO.setApi(httpOpenApi.group() + "." + method.getName());
        childDTO.getChildren().add(pointDTO);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
